package com.fy.opengltest.renderPicture;

import android.content.Context;
import android.net.Uri;
import android.opengl.GLES20;
import android.opengl.Matrix;

import com.fy.opengltest.Car360.IObject;
import com.fy.opengltest.MatrixUtils.Camera;
import com.fy.opengltest.MatrixUtils.Model;
import com.fy.opengltest.MatrixUtils.Projection;
import com.fy.opengltest.R;
import com.fy.opengltest.ShaderUtils;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

/**
 * Created by android on 8/18/17.
 */

public class FishEyeObject extends IObject{
    private Context context;
    private Uri uri;
    private int programId;
    private int aPositionHandle;
    private int uMatrixHandle;

    private int textureId;
    private int uTextureSamplerHandle;
    private int aTextureCoordHandle;

    private FloatBuffer vertexBuffer;
    private ShortBuffer indexBuffer;
    private FloatBuffer textureVertexBuffer;

    private final float[] vertexData = {
            1f, 1f, 0f,
            -1f, 1f, 0f,
            -1f, -1f, 0f,
            1f, -1f, 0f
    };

    private final short[] indexData = {
            0, 1, 2,
            2, 3, 0
    };

    private final float[] textureVertexData = {
            1f, 0f,
            0f, 0f,
            0f, 1f,
            1f, 1f
    };

    private Projection projection = new Projection(Projection.TYPE_frustumM);
    private Camera camera = new Camera();
    private Model model = new Model(0, 0, 2f, 0, 0, 0, 0, 0, 0);

    private float[] mMVMatrix = new float[16];
    private float[] mMVPMatrix = new float[16];

    public FishEyeObject(Context context) {
        this.context = context;
    }

    public FishEyeObject(Context context, Uri uri) {
        this.context = context;
        this.uri = uri;
    }

    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        String vertexShader = ShaderUtils.readRawTextFile(context, R.raw.eye_vert);
        String fragmentShader = ShaderUtils.readRawTextFile(context, R.raw.eye_frag);
        programId = ShaderUtils.createProgram(vertexShader, fragmentShader);
        aPositionHandle = GLES20.glGetAttribLocation(programId, "aPosition");
        uMatrixHandle = GLES20.glGetUniformLocation(programId, "uMatrix");

        if (null != uri)
            textureId = TextureHelper.loadTexture(context, uri);
        else
            textureId = TextureHelper.loadTexture(context, R.raw.dome190_24402400);//R.raw.demo_pic);

        uTextureSamplerHandle = GLES20.glGetUniformLocation(programId, "sTexture");
        aTextureCoordHandle = GLES20.glGetAttribLocation(programId, "aTexCoord");


        vertexBuffer = ByteBuffer.allocateDirect(vertexData.length * 4)
                .order(ByteOrder.nativeOrder())
                .asFloatBuffer()
                .put(vertexData);
        vertexBuffer.position(0);

        indexBuffer = ByteBuffer.allocateDirect(indexData.length * 2)
                .order(ByteOrder.nativeOrder())
                .asShortBuffer()
                .put(indexData);
        indexBuffer.position(0);

        textureVertexBuffer = ByteBuffer.allocateDirect(textureVertexData.length * 4)
                .order(ByteOrder.nativeOrder())
                .asFloatBuffer()
                .put(textureVertexData);
        textureVertexBuffer.position(0);
    }

    public void onSurfaceChanged(GL10 gl, int width, int height) {
//        BitmapFactory.Options options = new BitmapFactory.Options();
//        options.inJustDecodeBounds = true;
//        Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.test, options);
//        updateProjection(bitmap.getWidth(), bitmap.getHeight(), width, height);
//        bitmap.recycle();

        //updateProjection(9, 16, width, height);
        updateProjection(642, 402, width, height);
    }

    private void applyPos() {
        // This multiplies the view matrix by the model matrix, and stores the result in the MVP matrix
        // (which currently contains model * view).
        Matrix.multiplyMM(mMVMatrix, 0, camera.getMatrix(), 0, model.getMatrix(), 0);

        // This multiplies the model view matrix by the projection matrix, and stores the result in the MVP matrix
        // (which now contains model * view * projection).
        Matrix.multiplyMM(mMVPMatrix, 0, projection.getMartix(), 0, mMVMatrix, 0);
    }

    public void onDrawFrame(GL10 gl) {
        GLES20.glUseProgram(programId);
        applyPos();
        GLES20.glUniformMatrix4fv(uMatrixHandle, 1, false, mMVPMatrix, 0);
        GLES20.glEnableVertexAttribArray(aPositionHandle);
        GLES20.glVertexAttribPointer(aPositionHandle, 3, GLES20.GL_FLOAT, false, 12, vertexBuffer);


        GLES20.glEnableVertexAttribArray(aTextureCoordHandle);
        GLES20.glVertexAttribPointer(aTextureCoordHandle, 2, GLES20.GL_FLOAT, false, 8, textureVertexBuffer);

        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);

        GLES20.glUniform1i(uTextureSamplerHandle, 0);

        GLES20.glDrawElements(GLES20.GL_TRIANGLES, indexData.length, GLES20.GL_UNSIGNED_SHORT, indexBuffer);
    }

    private void updateProjection(int videoWidth, int videoHeight, int screenWidth, int screenHeight) {
        float screenRatio = (float) screenWidth / screenHeight;
        float videoRatio = (float) videoWidth / videoHeight;
        if (videoRatio > screenRatio) {
            projection.setLeft(-1f);
            projection.setRight(1f);
            projection.setBottom(-videoRatio / screenRatio);
            projection.setTop(videoRatio / screenRatio);
            projection.setNear(1f);
            projection.setFar(100f);
        } else {
            projection.setLeft(-1f);
            projection.setRight(1f);
            projection.setBottom(-screenRatio / videoRatio);
            projection.setTop(screenRatio / videoRatio);
            projection.setNear(1f);
            projection.setFar(100f);
        }
    }

    public void handValueChange(int type, int index, float change) {
        switch (type) {
            case 0:
                handCamera(index, change);
                break;
            case 1:
                handMode(index, change);
                break;
        }
    }

    private void handMode(int param, float change) {
        switch (param) {
            case 0:
                model.setX(change);
                break;
            case 1:
                model.setY(change);
                break;
            case 2:
                model.setZ(change);
                break;
            case 3:
                model.setAngleX(change);
                break;
            case 4:
                model.setAngleY(change);
                break;
            case 5:
                model.setAngleZ(change);
                break;
            case 6:
                model.setPitch(change);
                break;
            case 7:
                model.setYaw(change);
                break;
            case 8:
                model.setRoll(change);
                break;
        }
    }

    private void handCamera(int param, float change) {
        switch (param) {
            case 0:
                camera.setEyeX(change);
                break;
            case 1:
                camera.setEyeY(change);
                break;
            case 2:
                camera.setEyeZ(change);
                break;
            case 3:
                camera.setLookX(change);
                break;
            case 4:
                camera.setLookY(change);
                break;
            case 5:
                camera.setLookZ(change);
                break;
            case 6:
                camera.setUpX(change);
                break;
            case 7:
                camera.setUpY(change);
                break;
            case 8:
                camera.setUpZ(change);
                break;
        }
    }

    public String getParams() {
        return camera.toString() + "\n" + model.toString();
    }

    public float getValue(int type, int index) {
        switch (type) {
            case 0:
                return getCameraParam(index);
            case 1:
                return getModelParam(index);
            default:
                return 0f;
        }
    }


    private float getModelParam(int param) {
        float ret = 0f;
        switch (param) {
            case 0:
                ret = model.getX();
                break;
            case 1:
                ret = model.getY();
                break;
            case 2:
                ret = model.getZ();
                break;
            case 3:
                ret = model.getAngleX();
                break;
            case 4:
                ret = model.getAngleY();
                break;
            case 5:
                ret = model.getAngleZ();
                break;
            case 6:
                ret = model.getPitch();
                break;
            case 7:
                ret = model.getYaw();
                break;
            case 8:
                ret = model.getRoll();
                break;
        }
        return ret;
    }

    private float getCameraParam(int param) {
        float ret = 0f;
        switch (param) {
            case 0:
                ret = camera.getEyeX();
                break;
            case 1:
                ret = camera.getEyeY();
                break;
            case 2:
                ret = camera.getEyeZ();
                break;
            case 3:
                ret = camera.getLookX();
                break;
            case 4:
                ret = camera.getLookY();
                break;
            case 5:
                ret = camera.getLookZ();
                break;
            case 6:
                ret = camera.getUpX();
                break;
            case 7:
                ret = camera.getUpY();
                break;
            case 8:
                ret = camera.getUpZ();
                break;
        }
        return ret;
    }
}
